/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.document;

import cn.ac.nercis.pes.common.event.ProgressEvent;
import cn.ac.nercis.pes.common.listener.ProgressListener;
import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.model.bo.doc.SchemeDataBO;
import cn.ac.nercis.pes.model.bo.doc.common.DocRequest;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.DocMessageType;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.repository.dal.project.SystemEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemEvaluationDO;
import cn.ac.nercis.pes.service.document.common.DocDataService;
import cn.ac.nercis.pes.service.document.scheme.SchemeDataService;
import cn.ac.nercis.pes.service.utils.DocServerUtils;
import cn.ac.nercis.pes.service.utils.FileManage;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Objects;

/**
 * 密评方案生成服务类
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class PesSchemeService {
    private final static ProgressEvent PROGRESS_EVENT = new ProgressEvent("密码测评方案");

    private final SchemeDataService schemeDataService;
    private final DocDataService docDataService;
    private final SystemRepository systemRepository;
    private final SystemEvaluationRepository systemEvaluationRepository;

    private ProgressListener progressListener;

    @Autowired
    public PesSchemeService(SchemeDataService schemeDataService,
                            DocDataService docDataService,
                            SystemRepository systemRepository,
                            SystemEvaluationRepository systemEvaluationRepository) {
        this.schemeDataService = schemeDataService;
        this.docDataService = docDataService;
        this.systemRepository = systemRepository;
        this.systemEvaluationRepository = systemEvaluationRepository;

        this.progressListener = (event, args) -> {};
    }

    /**
     * 设置事件通知处理方法
     * @param event 事件处理方法
     */
    public final void setOnProgress(ProgressListener event){
        if(Objects.nonNull(event)){
            this.progressListener = event;
        }
    }

    /**
     * 生成密评方案
     * @param systemId 系统Id
     * @param filePath 方案文件存储路径
     * @return 报告生成结果
     */
    public Result<String> generateScheme(@NonNull String systemId, File filePath){
        if(!DocServerUtils.testConnect()){
            return Result.failed("文档生成服务未启动，无法生成密评方案！");
        }
        SystemDO systemDO = systemRepository.findById(systemId).orElse(null);
        if(Objects.isNull(systemDO)){
            return Result.failed(ResultCode.SYSTEM_NOT_EXIST);
        }
        var evaluationDO = systemEvaluationRepository.findBySystemId(systemId).orElse(null);
        if(Objects.isNull(evaluationDO)){
            return Result.failed("缺少系统测评基础信息！");
        }
        try{
            FileManage.createDirectory(filePath.getParentFile());
            DocRequest request = DocRequest.builder()
                    .messageType(DocMessageType.PES_SCHEME.getType())
                    .savePath(filePath.getAbsolutePath())
                    .build();
            var reportData = buildSchemeData(systemDO,evaluationDO);
            request.setContent(JsonUtils.toJson(reportData));
            processNotify(0.9,"即将生成完成...");
            return DocServerUtils.generateDocument(request);
        }catch (Exception ex){
            log.error("密评方案生成错误：",ex);
            return Result.failed(ex.getMessage());
        }
    }

    private SchemeDataBO buildSchemeData(SystemDO systemDO, SystemEvaluationDO evaluationDO) throws Exception{
        SchemeDataBO schemeDataBO = new SchemeDataBO();
        processNotify(0.2);
        schemeDataBO.setReportCode(schemeDataService.getSchemeCode(systemDO));
        schemeDataBO.setTestedUnitSystemName(schemeDataService.getTestedUnitSystemName(systemDO));
        schemeDataBO.setTestedUnit(schemeDataService.getTestedUnit(systemDO));
        schemeDataBO.setEvaluationUnit(schemeDataService.getEvaluationUnit());
        schemeDataBO.setEntrustedUnitName(schemeDataService.getEntrustedUnitName(systemDO));
        schemeDataBO.setHasEntrustedUnitName(StringUtils.isNotBlank(schemeDataBO.getEntrustedUnitName())
                && (!StringUtils.equals(schemeDataBO.getEntrustedUnitName(),schemeDataBO.getTestedUnit().getName())));
        schemeDataBO.setReportDate(docDataService.getReportDate(systemDO));

        processNotify(0.5);
        schemeDataBO.setSystem(schemeDataService.getSystem(systemDO,evaluationDO));
        schemeDataBO.setEvaluationBasis(docDataService.getEvaluationBasis(systemDO));
        schemeDataBO.setEvaluationProcess(docDataService.getEvaluationProcess(systemDO));
        schemeDataBO.setEvaluators(docDataService.getEvaluatorTable(systemDO));
        schemeDataBO.setSystemSituation(schemeDataService.getSystemSituation(systemDO));
        schemeDataBO.setEvaluationRange(docDataService.getEvaluationRange(systemDO));

        processNotify(0.6);

        schemeDataBO.setEvaluationPlan(schemeDataService.getEvaluationPlan(systemDO));
        schemeDataBO.setEvaluationContent(schemeDataService.getEvaluationContent(systemDO));
        schemeDataBO.setEvaluationAsset(schemeDataService.getEvaluationAsset(systemDO));
        return schemeDataBO;
    }

    private void processNotify(double value){
        processNotify(value,"");
    }

    private void processNotify(double value,String message){
        progressListener.notification(PROGRESS_EVENT, ProgressEvent.ProgressArgs.builder()
                .value(value)
                .message(message)
                .build());
    }
}
